#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>

#define NUM 1024 // 命令行最大长度
#define SIZE 32  // 参数最大数量
#define SEP " "  // 命令行参数分隔符

char cmd_line[NUM]; // 保存完整的命令行字符串
char *g_argv[SIZE]; // 保存打散之后的命令行字符串
char g_myval[64];   // 环境变量的buffer，用来测试

#define INPUT_REDIR 1  // 输入重定向标识
#define OUTPUT_REDIR 2 // 输出重定向标识
#define APPEND_REDIR 3 // 追加重定向标识
#define NONE_REDIR 0   // 无重定向标识

int redir_status = NONE_REDIR; // 重定向状态

char *CheckRedir(char *start)
{
    assert(start);
    char *end = start + strlen(start) - 1; // 指向字符串尾部

    // 从后往前遍历字符串查找重定向符号
    while (end >= start)
    {
        if (*end == '>')
        {
            if (*(end - 1) == '>')
            {
                // 处理追加重定向（>>）
                redir_status = APPEND_REDIR;
                *(end - 1) = '\0'; // 截断字符串，将 >> 替换为 \0
                end++;
                break;
            }
            // 处理输出重定向（>）
            redir_status = OUTPUT_REDIR;
            *end = '\0'; // 截断字符串，将 > 替换为 \0
            end++;
            break;
        }
        else if (*end == '<')
        {
            // 处理输入重定向（<）
            redir_status = INPUT_REDIR;
            *end = '\0';
            end++;
            break;
        }
        else
        {
            end--;
        }
    }
    while (*end == ' ')
    {
        end++;
    }

    // 如果找到了重定向符号，则返回指向重定向文件名的指针
    if (end >= start)
    {
        return end;
    }
    else
    {
        return NULL;
    }
}

int main()
{
    extern char **environ;
    while (1)
    {
        printf("[myShell]$ ");
        fflush(stdout);
        memset(cmd_line, '\0', sizeof(cmd_line));
        if (fgets(cmd_line, NUM, stdin) == NULL)
        {
            continue;
        }
        cmd_line[strlen(cmd_line) - 1] = '\0';
        char *sep = CheckRedir(cmd_line);
        g_argv[0] = strtok(cmd_line, SEP);
        
        if (g_argv[0] == NULL)
        {
            continue; // 如果没有输入命令，则继续等待输入
        }

        int index = 1;
        if (strcmp(g_argv[0], "ls") == 0 || strcmp(g_argv[0], "ll") == 0)
        {
            if (strcmp(g_argv[0], "ll") == 0)
            {
                g_argv[0] = "ls";
                g_argv[index++] = "-l";
            }
            g_argv[index++] = "--color=auto";
        }

        while (g_argv[index++] = strtok(NULL, SEP));
        g_argv[index] = NULL; // 确保最后一个元素是 NULL

        // 处理内置命令
        if (strcmp(g_argv[0], "export") == 0 && g_argv[1] != NULL)
        {
            // 验证环境变量赋值格式
            char *equals = strchr(g_argv[1], '=');
            if (equals == NULL)
            {
                printf("Invalid environment variable assignment format.\n");
                continue;
            }

            // 设置环境变量
            strncpy(g_myval, g_argv[1], sizeof(g_myval) - 1);
            g_myval[sizeof(g_myval) - 1] = '\0'; // 确保字符串以空字符终止
            if (getenv(g_myval) == 0)
            {
                printf("%s export success\n", g_myval);
            }
            else
            {
                perror("Failed to export variable");
            }
            continue;
        }

        if (strcmp(g_argv[0], "cd") == 0)
        {
            if (g_argv[1] != NULL)
            {
                if (chdir(g_argv[1]) == -1)
                {
                    perror("Failed to change directory");
                }
            }
            continue;
        }

        pid_t id = fork();
        if (id == 0)
        {
            if (sep != NULL)
            {
                int fd = -1;
                switch (redir_status)
                {
                case INPUT_REDIR:
                    fd = open(sep, O_RDONLY);
                    dup2(fd, 0);
                    break;
                case OUTPUT_REDIR:
                    fd = open(sep, O_WRONLY | O_CREAT | O_TRUNC, 0644);
                    dup2(fd, 1);
                    break;
                case APPEND_REDIR:
                    fd = open(sep, O_WRONLY | O_CREAT | O_APPEND, 0644);
                    dup2(fd, 1);
                    break;
                }
                if (fd != -1)
                {
                    close(fd);
                }
            }
            if (execvp(g_argv[0], g_argv) == -1)
            {
                perror("Failed to execute command");
            }

            exit(1);
        }
        else
        {
            int status = 0;
            waitpid(id, &status, 0);
            if (WIFEXITED(status))
            {
                printf("child process exit with code %d\n", WEXITSTATUS(status));
            }
        }
    }
    return 0;
}